package com.zbcn.duplicaterequest.interceptor;

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.zbcn.duplicaterequest.annotation.LocalLock;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

/**
 * 本地缓存来做 重复提交拦截
 */
@Aspect
@Configuration
public class LocalLockMethodInterceptor {

	//创建缓存
	private static final Cache<String, Object> CACHES = CacheBuilder.newBuilder()
			//缓存1000个
			.maximumSize(1000)
			// 设置写缓存后 5 秒钟过期
			.expireAfterWrite(5, TimeUnit.SECONDS)
			.build();

	// public 方法并且添加@LocalLock 注解
	@Around("execution(public * *(..)) && @annotation(com.zbcn.duplicaterequest.annotation.LocalLock)")
	public Object interceptor(ProceedingJoinPoint pjp){

		MethodSignature signature  = (MethodSignature)pjp.getSignature();
		Method method = signature.getMethod();
		
		LocalLock localLock = method.getAnnotation(LocalLock.class);

		String key = getKey(localLock.key(), pjp.getArgs());
		if (!StringUtils.isEmpty(key)) {
			if (CACHES.getIfPresent(key) != null) {
				throw new RuntimeException("请勿重复请求");
			}
			// 如果是第一次请求,就将 key 当前对象压入缓存中
			CACHES.put(key, key);
		}
		try {
			return pjp.proceed();
		} catch (Throwable throwable) {
			throw new RuntimeException("服务器异常");
		} finally {
			// TODO 为了演示效果,这里就不调用 CACHES.invalidate(key); 代码了
			//CACHES.invalidate(key);
		}

	}

	/**
	 * key 的生成策略,如果想灵活可以写成接口与实现类的方式
	 * @param keyExpress
	 * @param args
	 * @return
	 */
	private String getKey(String keyExpress, Object[] args) {
		for (int i = 0; i < args.length; i++) {
			keyExpress = keyExpress.replace("arg[" + i + "]", args[i].toString());
		}
		return keyExpress;
	}
}
